﻿//===============================================================================
// Jammer.NET Development Library
// Jmr.Entity.Poco
//===============================================================================
// Copyright © Junaid Fakhruddin, Matthew Leedham and Randolph Cabral.  
// All rights reserved.
//===============================================================================
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================

using System;
using System.Collections.Generic;
using System.Linq;

namespace Jmr.Entity.Poco
{
	/// <summary>
	/// IdentityMap class.  Provides identity mapping features as described in PoEAA (p.195).
	/// http://martinfowler.com/eaaCatalog/identityMap.html
	/// </summary>
	public class IdentityMap : Dictionary<string, object> { }

	/// <summary>
	/// IdentityMapFinder class.  Provides identity map search features as described in PoEAA (p.195).
	/// http://martinfowler.com/eaaCatalog/identityMap.html
	/// </summary>
	public class IdentityMapFinder
	{
		/// <summary>
		/// Generates a hashkey given the entity's primary key property values and the argument entity.
		/// </summary>
		/// <typeparam name="TEntity">The entity type.</typeparam>
		/// <param name="entityarg">The argument entity.</param>
		/// <param name="typemap">The DatabaseMap for the entity</param>
		/// <param name="argmap">The DatabaseMap for the argument entity.</param>
		/// <returns>Returns the hashkey string value.</returns>
		public static string GenerateHashKey<TEntity>(object entityarg, DatabaseMap typemap, DatabaseMap argmap)
		{
			return GenerateHashKey(typeof(TEntity), entityarg, typemap, argmap);
		}

		/// <summary>
		/// Generates a hashkey given the entity's primary key property values and the argument entity.
		/// </summary>
		/// <param name="type">The entity type.</param>
		/// <param name="entityarg">The argument entity.</param>
		/// <param name="typemap">The DatabaseMap for the entity</param>
		/// <param name="argmap">The DatabaseMap for the argument entity.</param>
		/// <returns>Returns the hashkey string value.</returns>
		public static string GenerateHashKey(Type type, object entityarg, DatabaseMap typemap, DatabaseMap argmap)
		{
			var ret = type.Name;
			var typekeys = typemap.ColumnMaps.Where(m => m.IsPrimaryKey).OrderBy(m => m.PropertyName);

			foreach (var key in typekeys)
			{
				var propertyname = argmap.ColumnMaps.Single(m => m.ColumnName == key.ColumnName).PropertyName;
				ret += "." + entityarg.GetType().GetProperty(propertyname).GetValue(entityarg, null).ToString();
			}

			return ret;
		}

		/// <summary>
		/// Finds the reference of an entity stored in the identity map.
		/// </summary>
		/// <typeparam name="TEntity">Type of entity to find.</typeparam>
		/// <param name="entityarg">The argument entity.</param>
		/// <param name="idmap">The IdentityMap to search.</param>
		/// <param name="typemap">The DatabaseMap for the entity.</param>
		/// <param name="argmap">The DatabaseMap for the argument entity.</param>
		/// <returns>Returns the reference of an entity stored in the identity map.</returns>
		public TEntity FetchMatch<TEntity>(object entityarg, IdentityMap idmap, DatabaseMap typemap, DatabaseMap argmap)
		{
			return (TEntity)FetchMatch(typeof(TEntity), entityarg, idmap, typemap, argmap);
		}

		/// <summary>
		/// Finds the reference of an entity stored in the identity map.
		/// </summary>
		/// <param name="type">Type of entity to find.</param>
		/// <param name="entityarg">The argument entity.</param>
		/// <param name="idmap">The IdentityMap to search.</param>
		/// <param name="typemap">The DatabaseMap for the entity.</param>
		/// <param name="argmap">The DatabaseMap for the argument entity.</param>
		/// <returns>Returns the reference of an entity stored in the identity map.</returns>
		public object FetchMatch(Type type, object entityarg, IdentityMap idmap, DatabaseMap typemap, DatabaseMap argmap)
		{
			var hashkey = GenerateHashKey(type, entityarg, typemap, argmap);
			var results = idmap.Where(m => m.Key == hashkey);
			var ret = (results.Count() == 1) ? results.FirstOrDefault().Value : null;
			return ret;
		}

		/// <summary>
		/// Finds the reference of an entity stored in the identity map.
		/// </summary>
		/// <typeparam name="TEntity">Type of entity to find.</typeparam>
		/// <param name="hashkey">The HashKey string.</param>
		/// <param name="idmap">The IdentityMap to search.</param>
		/// <returns>Returns the reference of an entity stored in the identity map.</returns>
		public TEntity FetchByHashKey<TEntity>(string hashkey, IdentityMap idmap)
		{
			return (TEntity)FetchByHashKey(typeof(TEntity), hashkey, idmap);
		}

		/// <summary>
		/// Finds the reference of an entity stored in the identity map.
		/// </summary>
		/// <param name="type">Type of entity to find.</param>
		/// <param name="hashkey">The HashKey string.</param>
		/// <param name="idmap">The IdentityMap to search.</param>
		/// <returns>Returns the reference of an entity stored in the identity map.</returns>
		public object FetchByHashKey(Type type, string hashkey, IdentityMap idmap)
		{
			return idmap.Single(m => m.Key == hashkey).Value;
		}
	}
}
